#include "proc.h"
#include "riscv.h"
#include "console.h"
#include "mmap.h"
#include "trap.h"
#include "sound.h"


extern char trampoline[], uservec[], userret[];
void kernelvec();

void trap_init(){
  write_csr(stvec, kernelvec);
  write_csr(sscratch, 0);
  set_csr(sstatus, SSTATUS_SUM);
}

static void handle_syscall(struct trapframe * tf)
{
  tf->epc += 4;
  intr_on();
  tf->a0 = do_syscall(tf->a0, tf->a1, tf->a2, tf->a3,
                           tf->a4, tf->a5, tf->a7);
}



void dump_tf(struct trapframe* tf)
{
  /*   0 */ uint64 kernel_satp;   // kernel page table
  /*   8 */ uint64 kernel_sp;     // top of process's kernel stack
  /*  16 */ uint64 kernel_trap;   // usertrap()
  /*  24 */ uint64 epc;           // saved user program counter
  printk("kernel_satp %p    kernel_sp %p\t",tf->kernel_satp,tf->kernel_sp);
  printk("kernel_trap %p    epc %p\n",tf->kernel_trap,tf->epc);
  /*  32 */ uint64 kernel_hartid; // saved kernel tp
  /*  40 */ uint64 ra;
  /*  48 */ uint64 sp;
  /*  56 */ uint64 gp;
  printk("kernel_hartid %p    ra %p\t",tf->kernel_hartid,tf->ra);
  printk("sp %p    gp %p\n",tf->sp,tf->gp);
  /*  64 */ uint64 tp;
  /*  72 */ uint64 t0;
  /*  80 */ uint64 t1;
  /*  88 */ uint64 t2;
  printk("tp %p    t0 %p\t",tf->tp,tf->t0);
  printk("t1 %p    t2 %p\n",tf->t1,tf->t2);
  /*  96 */ uint64 s0;
  /* 104 */ uint64 s1;
  /* 112 */ uint64 a0;
  /* 120 */ uint64 a1;
  printk("s0 %p    s1 %p\t",tf->s0,tf->s1);
  printk("a0 %p    a1 %p\n",tf->a0,tf->a1);
  /* 128 */ uint64 a2;
  /* 136 */ uint64 a3;
  /* 144 */ uint64 a4;
  /* 152 */ uint64 a5;
  printk("a2 %p    a3 %p\t",tf->a2,tf->a3);
  printk("a4 %p    a5 %p\n",tf->a4,tf->a5);
  /* 160 */ uint64 a6;
  /* 168 */ uint64 a7;
  /* 176 */ uint64 s2;
  /* 184 */ uint64 s3;
  printk("a6 %p    a7 %p\t",tf->a6,tf->a7);
  printk("s2 %p    s3 %p\n",tf->s2,tf->s3);
  /* 192 */ uint64 s4;
  /* 200 */ uint64 s5;
  /* 208 */ uint64 s6;
  /* 216 */ uint64 s7;
  printk("s4 %p    s5 %p\t",tf->s4,tf->s5);
  printk("s6 %p    s7 %p\n",tf->s6,tf->s7);
  /* 224 */ uint64 s8;
  /* 232 */ uint64 s9;
  /* 240 */ uint64 s10;
  /* 248 */ uint64 s11;
  printk("s8 %p    s9 %p\t",tf->s8,tf->s9);
  printk("s10 %p    s11 %p\n",tf->s10,tf->s11);
  /* 256 */ uint64 t3;
  /* 264 */ uint64 t4;
  /* 272 */ uint64 t5;
  /* 280 */ uint64 t6;
  printk("t3 %p    t4 %p\t",tf->t3,tf->t4);
  printk("t5 %p    t6 %p\n",tf->t5,tf->t6);
  printk("stval %lx ;\n\t",r_stval());
  printk("sstatus %x ; \n",r_sstatus());
  printk("scause %x ; \n",r_scause());

}







static void handle_breakpoint(struct trapframe* tf)
{
  dump_tf(tf);
  printk("Breakpoint!\n");
  tf->epc += 4;
}

static void handle_misaligned_fetch(struct trapframe* tf)
{
  dump_tf(tf);
  panic("Misaligned instruction access!");
}

static void segfault(struct trapframe* tf, uintptr_t addr, const char* type)
{
//  your code here:
//  打印trapframe
//  判断是内核空间还是用户空间
//  修改panic信息
 	panic("you need add your code!");
}

static void handle_fault_fetch(struct trapframe* tf)
{
  uint64 badvaddr=r_stval();
  if (handle_page_fault(badvaddr, PROT_EXEC) != 0)
    segfault(tf, badvaddr, "fetch");
}

static void handle_fault_load(struct trapframe* tf)
{
  uint64 badvaddr=r_stval();
  if (handle_page_fault(badvaddr, PROT_READ) != 0)
    segfault(tf, badvaddr, "load");
}

static void handle_fault_store(struct trapframe* tf)
{
  uint64 badvaddr=r_stval();
  if (handle_page_fault(badvaddr, PROT_WRITE) != 0)
    segfault(tf, badvaddr, "store");
}


void handle_trap(struct trapframe* tf)
{

  typedef void (*trap_handler)(struct trapframe*);

  const static trap_handler trap_handlers[] = {
    [CAUSE_MISALIGNED_FETCH] = handle_misaligned_fetch,
    [CAUSE_FETCH_PAGE_FAULT] = handle_fault_fetch,
    [CAUSE_BREAKPOINT] = handle_breakpoint,
    [CAUSE_LOAD_PAGE_FAULT] = handle_fault_load,
    [CAUSE_STORE_PAGE_FAULT] = handle_fault_store,
  };

  if( r_scause() < ARRAY_SIZE(trap_handlers) ){
     if(DEBUG_TRAPU)
        printk("scause %d\n",r_scause());
     trap_handlers[r_scause()](tf); }
  else
  {
    printk("usertrap(): unexpected scause %p pid=%d\n", r_scause(), myproc()->pid);
    printk("            sepc=%p stval=%p\n", r_sepc(), r_stval());

  }
}

static void handle_interrupt(struct trapframe* tf)
{
  extern volatile size_t ticks;
  ssize_t cause = (read_csr(scause) << 1) >> 1;
  //  printk("tf->cause %x\n",(intptr_t)tf->cause);
  if(cause==IRQ_S_SOFT){
   // printk("tf->cause %x\n IRQ_S_SOFT",(ssize_t)cause);
   sound_recv();
   w_sip(r_sip() & ~MIP_SSIP);
  }

}

void
usertrap(void)
{
  if(DEBUG_TRAPU||DEBUG){
     struct proc *p = myproc();
     printk("in user trap scause %d \n",r_scause());
     printk("in user trap sie %d \n",r_sie());
    //  uvmprint(p->pagetable);
     proc_trapframe_print(p);
  }

  int which_dev = 0;

  if((r_sstatus() & SSTATUS_SPP) != 0)
    panic("usertrap: not from user mode");

  // send interrupts and exceptions to kerneltrap(),
  // since we're now in the kernel.
  w_stvec((uint64)kernelvec);

  struct proc *p = myproc();

  // save user program counter.
  p->trapframe->epc = r_sepc();
  ssize_t scause=r_scause();

  if( scause== 8){
    // system call

    // if(p->killed)
    //   exit(-1);
    // sepc points to the ecall instruction,
    // but we want to return to the next instruction.
    // p->trapframe->a0=0;
    // an interrupt will change sstatus &c registers,
    // so don't enable until done with those registers.
      // intr_on();

     //panic("user   trap syscall\n");
     handle_syscall(p->trapframe);
   } else if(scause<0){
      handle_interrupt(p->trapframe);
   }else
   {
      handle_trap(p->trapframe);
   }

  // else if((which_dev = devintr()) != 0){
  //   // ok
  // } else {
  //   printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
  //   printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
  //   p->killed = 1;
  // }

  // if(p->killed)
  //   exit(-1);

  // give up the CPU if this is a timer interrupt.
  // if(which_dev == 2)
  //   yield();

  usertrapret();
}

void
usertrapret(void)
{
  struct proc *p = myproc();


  // we're about to switch the destination of traps from
  // kerneltrap() to usertrap(), so turn off interrupts until
  // we're back in user space, where usertrap() is correct.
  intr_off();

  // send syscalls, interrupts, and exceptions to trampoline.S
  w_stvec((uint64)uservec);

  // set up trapframe values that uservec will need when
  // the process next re-enters the kernel.
  p->trapframe->kernel_satp = r_satp();         // kernel page table
  p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
  p->trapframe->kernel_trap = (uint64)usertrap;
  p->trapframe->kernel_hartid = r_tp();         // hartid for cpuid()

  // set up the registers that trampoline.S's sret will use
  // to get to user space.

  // set S Previous Privilege mode to User.
  unsigned long x = r_sstatus();
  x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
  x |= SSTATUS_SPIE; // enable interrupts in user mode
  w_sstatus(x);

  // set S Exception Program Counter to the saved user pc.
  w_sepc(p->trapframe->epc);

  // tell trampoline.S the user page table to switch to.
  uint64 satp = MAKE_SATP(p->pagetable);

  // jump to trampoline.S at the top of memory, which
  // switches to the user page table, restores user registers,
  // and switches to user mode with sret.
  // uvmprint(p->pagetable);
  uint64 fn = (uint64) userret ;
  if(DEBUG_TRAPU){
      printk("------------in userret\n");
      printk("stvec %p\n",r_stvec());
      // timer_print();
      // proc_trapframe_print(p);

  }
  ((void (*)(uint64,uint64))fn)((uint64)p->trapframe, satp);
}



// interrupts and exceptions from kernel code go here via kernelvec,
// on whatever the current kernel stack is.
void
kerneltrap()
{
  if(DEBUG_TRAPK|DEBUG){
    struct proc *p = myproc();
    printk("in kernel trap scause %d \n",r_scause());
    uvmprint(p->pagetable);
    proc_trapframe_print(p);
    panic("in kernel trap\n");
  }

  int which_dev = 0;
  uint64 sepc = r_sepc();
  uint64 sstatus = r_sstatus();
  uint64 scause = r_scause();
  if ((ssize_t)scause < 0) handle_interrupt(0);   


  if((sstatus & SSTATUS_SPP) == 0)
    panic("kerneltrap: not from supervisor mode");
  if(intr_get() != 0)
    panic("kerneltrap: interrupts enabled");

  // if((which_dev = devintr()) == 0){
  //   printf("scause %p\n", scause);
  //   printf("sepc=%p stval=%p\n", r_sepc(), r_stval());
  //   panic("kerneltrap");
  // }

  // give up the CPU if this is a timer interrupt.
  // if(which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING)
  //   yield();

  // the yield() may have caused some traps to occur,
  // so restore trap registers for use by kernelvec.S's sepc instruction.
  w_sepc(sepc);
  w_sstatus(sstatus);
}


